package gohandler

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go-micro-framework/go_service/core/gomodel"
	"go-micro-framework/go_service/core/goutils"
	"go-micro.dev/v4/logger"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type HandlerEntity struct {
	//启动启用排除字符串，支持多个，中间英文用豆包分隔
	Exclude        string
	ServiceName    string //服务名称，用于打印日志记录
	ParamsSecret   string //请求参数加密协商编码
	md5IndexSecret []int  //请求参数加密协商编码
}

func GinLoggerHandler(handler *HandlerEntity) gin.HandlerFunc {
	var order string = "1"
	return func(ctx *gin.Context) {
		t := time.Now()
		// 设置随机数种子
		var logName = handler.ServiceName + "_" + time.DateTime + "_" + strconv.Itoa(rand.Intn(10000))
		// 设置 example 变量
		ctx.Set("logName", "logName")
		// 请求前
		logger.Infof("Logger Middleware [LOG_ServiceName]=:[% s] Before request time now:[%s]", logName, t)
		//如果排除些验证,即立刻进入下一个验证,并结束当前验证
		if contains(handler.Exclude, order) {
			logger.Info("Logger Middleware: exclude request order=", 1)
			ctx.Next()
			return
		}

		ctx.Next()
		// 请求后
		latency := time.Since(t)
		// 获取发送的 status
		status := ctx.Writer.Status()
		RemoveActionHttpRequest()
		logger.Infof("Logger Middleware [LOG_ServiceName]=:[%s] After request time Since:[%s],c.Writer.Status:[%d]", logName, latency, status)
	}
}

// 自定义中间件：身份验证中间件
func GinWhiteHandler(handler *HandlerEntity) gin.HandlerFunc {
	// 这里省略了身份验证的具体逻辑
	var order = "2"
	return func(ctx *gin.Context) {
		//如果排除些验证,即立刻进入下一个验证,并结束当前验证
		if contains(handler.Exclude, order) {
			logger.Info("White Middleware: exclude request order=", 2)
			ctx.Next()
			return
		}
		logger.Info("White Middleware: Before request")
		// 调用下一个中间件或处理器
		var check = false
		if check { //如果是系统区别白名单调过下面的验证
			return
		}
		ctx.Next()

		logger.Info("White Middleware: After request")
	}
}

// 自定义中间件：黑名单验证中间件
func GinBlackHandler(handler *HandlerEntity) gin.HandlerFunc {
	// 这里省略了身份验证的具体逻辑
	var order string = "3"
	return func(ctx *gin.Context) {
		//如果排除些验证,即立刻进入下一个验证,并结束当前验证
		if contains(handler.Exclude, order) {
			logger.Info("Black Middleware: exclude request order=", 3)
			ctx.Next()
			return
		}

		logger.Info("Black Middleware: Before request")
		// 调用下一个中间件或处理器
		var check = false
		if check { //如果是系统级别的黑名跳调过下面的验证,返回错误
			return
		}
		// 调用下一个中间件或处理器
		ctx.Next()
		// 请求后
		logger.Info("Black Middleware: After request")
	}
}

// 自定义中间件：表单提交中间件,不允许排除
func GinUserActionHandler(handler *HandlerEntity) gin.HandlerFunc {
	// 这里省略了身份验证的具体逻辑
	var order string = "5"
	var formHandler = func(ctx *gin.Context) {
		//如果排除些验证,即立刻进入下一个验证,并结束当前验证
		if contains(handler.Exclude, order) {
			logger.Info("Form Middleware: exclude request order=", 5)
			ctx.Next()
			return
		}
		logger.Info("Form Middleware: Before request")
		var paramErr, err = InitActionHttpRequest(ctx)
		if err != nil && paramErr != nil { //异常时,结束下次验证
			var response = gomodel.ErrorFormatMsg(paramErr, err.Error())
			ctx.Abort()
			ctx.JSON(http.StatusOK, response)
			RemoveActionHttpRequest()
			return
		}

		// 调用下一个中间件或处理器
		ctx.Next()
		// 请求后
		logger.Info("Form Middleware: After request")
	}
	RemoveActionHttpRequest()
	return formHandler
}
func GinTokenHandler(handler *HandlerEntity) gin.HandlerFunc {
	var order string = "6"
	return func(ctx *gin.Context) {
		//如果排除些验证,即立刻进入下一个验证,并结束当前验证
		if contains(handler.Exclude, order) {
			logger.Info("Token Middleware: exclude request order=", 5)
			ctx.Next()
			return
		}

		// 请求前
		logger.Infof("token Middleware: Before request ")
		t := time.Now()
		var action = GetActionHttpRequest(ctx.FullPath())
		//终止请求
		if action == nil {
			var response = gomodel.ErrorCode(gomodel.SYS_PARAM_CHECK)
			ctx.AbortWithStatusJSON(http.StatusOK, response)
			//panic("Something went wrong")
			return
		}
		//post 请求才验证token get 请求和白名单跳过验证
		if !action.IsPost || action.GetUrlRemote().IsWhite {
			ctx.Next()
			return
		}
		var accessToken = action.GetPost().AccessToken
		//accessToken不存在 不存在
		if action.GetPost().UserId <= 0 || accessToken == "" {
			var response = gomodel.ErrorCode(gomodel.SYS_AUTH_ACCESS_TOKEN_FAIL)
			ctx.AbortWithStatusJSON(http.StatusOK, response)
			//panic("Something went wrong")
			return
		}
		//token time accessToken不存在 不存在或过期,提示用户重新登录
		var validAccessToken = isValidAccessToken(action)
		if validAccessToken.GetCode() != gomodel.SYS_SUCCESS.GetCode() {
			var response = gomodel.ErrorCode(validAccessToken)
			ctx.AbortWithStatusJSON(http.StatusOK, response)
			//panic("Something went wrong")
			return
		}
		ctx.Next()
		// 请求后
		latency := time.Since(t)
		// 获取发送的 status
		status := ctx.Writer.Status()
		logger.Infof("token Middleware: After request time Since:[%s],c.Writer.Status:[%d]", latency, status)
	}
}

func isValidAccessToken(action *gomodel.ActionHttpRequestRemote) *gomodel.ErrorCodeEnum {

	//accessToken不存在 不存在
	if action.GetPost().UserId <= 0 || action.GetPost().AccessToken == "" {
		return gomodel.SYS_AUTH_ACCESS_TOKEN_FAIL
	}
	var token = &goutils.Token{UserId: action.GetPost().UserId, AccessToken: action.GetPost().AccessToken}
	//token.SetUserTokenMapCache()
	var errNo = token.GetUserTokenMapCache()
	if errNo == 0 {
		return gomodel.SYS_SUCCESS
	} //错误提示信息
	return gomodel.SYS_AUTH_ACCESS_TOKEN_FAIL

}

func RedisHSetUserToken() {

}
func contains(exclude string, target string) bool {
	arr := strings.Split(exclude, ",")
	for _, value := range arr {
		if value == target {
			return true
		}
	}
	return false
}

// 自定义中间件：参数验证中间件
func GinParameterMd5SignHandler(handler *HandlerEntity) gin.HandlerFunc {
	var order string = "7"
	return func(ctx *gin.Context) {

		// 这里省略了身份验证的具体逻辑
		logger.Info("Parameter Md5Sign Middleware: Before request")
		//如果排除些验证,即立刻进入下一个验证,并结束当前验证
		if contains(handler.Exclude, order) {
			ctx.Next()
			return
		}
		var action = GetActionHttpRequest(ctx.FullPath())
		//post 请求才验证token
		if action == nil {
			var response = gomodel.ErrorCode(gomodel.SYS_PARAM_CHECK)
			ctx.Abort()
			ctx.JSON(http.StatusOK, response)
			//panic("Something went wrong")
			return
		}
		//get 请求和白名单跳过验证 || action.GetUrlRemote().IsWhite
		if action.GetUrlRemote().IsNotSign {
			ctx.Next()
			return
		}
		//排除过滤字段
		var exclude = action.GetUrlRemote().ExcludeParam
		var excludes = strings.Split(exclude, ",")
		excludes = append(excludes, "cliSign")

		var sortStr = ""
		//json 格式请求时，执行的验证
		if action.IsJsonRequest {
			entity, errorCode := GetActionHttpRequestJson(ctx)
			if errorCode.GetCode() != gomodel.SYS_SUCCESS.GetCode() {
				var response = gomodel.ErrorCode(errorCode)
				ctx.Abort()
				ctx.JSON(http.StatusOK, response)
			}
			sortStr = goutils.SortAndConcatJson(entity, excludes)
		} else { //form 表单提交请求
			// Values map[string][]string
			form := ctx.Request.Form
			sortStr = goutils.SortAndConcatParams(form, excludes)

		} //通过空判断，不存在或异常的情况，
		if sortStr == "" {
			var response = gomodel.ErrorCode(gomodel.SYS_PARAM_CHECK)
			ctx.Abort()
			ctx.JSON(http.StatusOK, response)
			return
		}
		var serverMd5Sign = goutils.EncryptMd5Params(sortStr, handler.ParamsSecret) //, handler.md5IndexSecret[0], handler.md5IndexSecret[1])
		var clientMd5Sign = action.GetPost().CliSign

		action.ServerMd5Sign = serverMd5Sign
		//执行当前拦截器实现的业务逻辑
		if (clientMd5Sign == "" || serverMd5Sign == "") || !strings.EqualFold(clientMd5Sign, serverMd5Sign) {
			var response = gomodel.ErrorFormatMsg(gomodel.SYS_PARAM_SIGN_FAIL, clientMd5Sign, serverMd5Sign)
			ctx.Abort()
			ctx.JSON(http.StatusOK, response)
			return
		}
		// 调用下一个中间件或处理器
		ctx.Next()
		logger.Info("Parameter Md5Sign Middleware: After request")
	}
}

// 使用 validate 架构进行参数验证，具体例子如下
/**
	type User struct {
		Name  string `json:"name" validate:"required"`
		Email string `json:"email" validate:"required,email"`
		Age   int    `json:"age" validate:"required,min=18"`
	}
**/
// 自定义中间件：参数验证中间件
func GinParameterHandler(handler *HandlerEntity) gin.HandlerFunc {
	var order string = "8"
	return func(cxt *gin.Context) {
		// 这里省略了身份验证的具体逻辑
		logger.Info("Parameter Middleware: Before request")
		//如果排除些验证,即立刻进入下一个验证,并结束当前验证
		if contains(handler.Exclude, order) {
			cxt.Next()
			return
		}
		var action = GetActionHttpRequest(cxt.FullPath())
		//post 请求才验证token
		if action == nil {
			var response = gomodel.ErrorCode(gomodel.SYS_PARAM_CHECK)
			cxt.Abort()
			cxt.JSON(http.StatusOK, response)
			//panic("Something went wrong")
			return
		}
		var err error
		validate := validator.New()
		if action.IsJsonRequest {
			entity, errorCode := GetActionHttpRequestJson(cxt)
			if errorCode.GetCode() != gomodel.SYS_SUCCESS.GetCode() {
				var response = gomodel.ErrorCode(errorCode)
				cxt.Abort()
				cxt.JSON(http.StatusOK, response)
			}
			err = validate.Struct(entity)

		} else {
			entity, ok := GetUrlHttpRequestEntityCache(cxt.FullPath())
			//如果不存在验证对象，返回正常,执行下一个验证
			if entity == nil || !ok {
				cxt.Next()
				logger.Info("Parameter Middleware: After request")
				return
			}
			cxt.ShouldBind(entity)
			err = validate.Struct(entity)
		}
		//ok,err = validateArrayStruct(validate,entity)
		if err == nil { //验证通过时，也执行下一次验证
			cxt.Next()
			logger.Info("Parameter Middleware: After request")
			return
		}
		var response = validateParameterError(err)
		cxt.Abort()
		cxt.JSON(http.StatusOK, response)
		logger.Info("Parameter Middleware: After request")
	}
}

type DemoJsonRequestVo struct {
	Name string `form:"name" validate:"required"`
	//Email string `json:"email" validate:"required,email"`
	Age int `form:"age" validate:"required,min=18"`
}

func validateArrayStruct(validate *validator.Validate, entities ...any) (bool, []error) {
	//// 使用反射判断值的类型是否为结构体
	//isStruct := reflect.TypeOf(entity).Kind() == reflect.Struct
	//// 使用反射判断值的类型是否为 []struct
	//value := reflect.ValueOf(entity)
	//isSliceOfStruct := value.Kind() == reflect.Slice && value.Type().Elem().Kind() == reflect.Struct

	if entities == nil || len(entities) == 0 {
		return true, nil
	}
	errs := make([]error, 0)
	if len(entities) == 1 {
		err := validate.Struct(entities[0])
		if err != nil {
			errs = append(errs, err)
		}
	} else {
		for _, entity := range entities {
			err := validate.Struct(entity)
			if err != nil {
				errs = append(errs, err)
			}
		}
	}
	if len(errs) == 0 {
		return true, nil
	}
	return false, errs
}

// 参数验证实现方法
func validateParameterError(err error) *gomodel.ResponseResultVo {
	validationErrors, ok := err.(validator.ValidationErrors)
	if !ok { //
		logger.Error("验证出错:", err)
		var response = gomodel.ErrorCode(gomodel.SYS_PARAM_CHECK)
		logger.Info("Parameter Middleware: After request")
		return response
	}
	var errBuilder = strings.Builder{}
	for _, e := range validationErrors {
		logger.Error("字段 %s 验证失败，错误信息: %s\n", e.Field(), e.ActualTag())
		var errStr = fmt.Sprintf("字段 %s 验证失败，错误信息: %s\n", e.Field(), e.ActualTag())
		errBuilder.WriteString(errStr)
	}
	var response = gomodel.ErrorFormatMsg(gomodel.SYS_PARAM_CHECK_FAIL, errBuilder.String())
	return response
}

// 自定义中间件：身份验证中间件
func GinVersionHandler(handler *HandlerEntity) gin.HandlerFunc {
	// 这里省略了身份验证的具体逻辑
	var order string = "9"
	return func(ctx *gin.Context) {
		logger.Info("Version Middleware: Before request")
		// 这里省略了身份验证的具体逻辑
		logger.Info("Parameter Middleware: Before request")
		//如果排除些验证,即立刻进入下一个验证,并结束当前验证
		if contains(handler.Exclude, order) {
			ctx.Next()
			return
		}
		// 调用下一个中间件或处理器
		ctx.Next()
		logger.Info("Version Middleware: After request")
	}
}
